﻿using ImageK.Gui;
using ImageK.Measure;
using ImageK.Plugin.Filter;
using ImageK.Plugin.Frame;
using ImageK.Process;
using ImageK.Util;
using ImageK;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;

namespace ImageK.Plugin
{
    /// <summary>
    /// This plugin implements the Analyze/Histogram command.
    /// </summary>
    public class Histogram : PlugIn {

	    private static bool staticUseImageMinAndMax = true;
        private static double staticXMin, staticXMax;
        private static String staticYMax = "Auto";
        private static bool staticStackHistogram;
        private static int imageID;
        private int nBins = 256;
        private bool useImageMinAndMax = true;
        private double xMin, xMax;
        private string yMax = "Auto";
        private bool stackHistogram;
        private CheckBox checkbox;
        private TextBox minField, maxField;
        private string defaultMin, defaultMax;

        public void run(string arg)
        {
            ImagePlus imp = IJ.getImage();
            int bitDepth = imp.getBitDepth();
            if (bitDepth==32 || IJ.altKeyDown() || (IJ.isMacro()&&Macro.GetOptions()!=null))
            {
                IJ.setKeyUp(Keys.Alt);
                if (!showDialog(imp))
                    return;
            }
            else
            {
                int stackSize = imp.getStackSize();
                bool noDialog = stackSize==1;
                if (stackSize==3)
                {
                    ImageStack stack = imp.getStack();
                    string label1 = stack.getSliceLabel(1);
                    if ("Hue".Equals(label1))
                        noDialog = true;
                }
                int flags = noDialog ? 0 : setupDialog(imp, 0);
                if (flags==BasePlugInFilter.DONE) return;
                stackHistogram = flags==BasePlugInFilter.DOES_STACKS;
                Calibration cal = imp.getCalibration();
                if (bitDepth==16 && ImagePlus.getDefault16bitRange()!=0)
                {
                    xMin = 0.0;
                    xMax = Math.Pow(2, ImagePlus.getDefault16bitRange())-1;
                    useImageMinAndMax = false;
                }
                else if (stackHistogram && ((bitDepth==8&&!cal.calibrated())||imp.isRGB()))
                {
                    xMin = 0.0;
                    xMax = 256.0;
                    useImageMinAndMax = false;
                }
                else
                    useImageMinAndMax = true;
                yMax = "Auto";
            }
            ImageStatistics stats = null;
            if (useImageMinAndMax)
            {
                xMin = 0.0;
                xMax = 0.0;
            }
            int iyMax = (int)Tools.parseDouble(yMax, 0.0);
            bool customHistogram = (bitDepth==8||imp.isRGB()) && (!(xMin==0.0&&xMax==0.0)||nBins!=256||iyMax>0);
            HistogramPlot plot = new HistogramPlot();
            if (stackHistogram || customHistogram)
            {
                ImagePlus imp2 = imp;
                if (customHistogram && !stackHistogram && imp.getStackSize()>1)
                    imp2 = new ImagePlus("Temp", imp.getProcessor());
                stats = new StackStatistics(imp2, nBins, xMin, xMax);
                stats.histYMax = iyMax;
                plot.draw(imp, stats);
            }
            else
                plot.draw(imp, nBins, xMin, xMax, iyMax);
            plot.Show();
        }

        bool showDialog(ImagePlus imp)
        {
            if (!IJ.isMacro())
            {
                nBins = HistogramWindow.nBins;
                useImageMinAndMax = staticUseImageMinAndMax;
                xMin=staticXMin; xMax=staticXMax;
                yMax = staticYMax;
                stackHistogram = staticStackHistogram;
            }
            ImageProcessor ip = imp.getProcessor();
            double min = ip.getMin();
            double max = ip.getMax();
            if (imp.getID()!=imageID || (min==xMin&&min==xMax))
                useImageMinAndMax = true;
            if (imp.getID()!=imageID || useImageMinAndMax)
            {
                xMin = min;
                xMax = max;
                Calibration cal = imp.getCalibration();
                xMin = cal.getCValue(xMin);
                xMax = cal.getCValue(xMax);
            }
            defaultMin = IJ.d2s(xMin, 2);
            defaultMax = IJ.d2s(xMax, 2);
            imageID = imp.getID();
            int stackSize = imp.getStackSize();
            GenericDialog gd = new GenericDialog("Histogram");
            gd.addNumericField("Bins:", nBins, 0);
            gd.addCheckbox("Use pixel value range", useImageMinAndMax);
            // gd.setInsets(5, 40, 10);
            gd.addMessage("or use:");
            int fwidth = 8;
            int nwidth = Math.Max(IJ.d2s(xMin, 2).Length, IJ.d2s(xMax, 2).Length);
            if (nwidth>fwidth) fwidth = nwidth;
            int digits = -2;  // use scientific notation if needed
            if (xMin==(int)xMin && xMax==(int)xMax)
                digits = 0;
            gd.addNumericField("X_min:", xMin, digits, fwidth, null);
            gd.addNumericField("X_max:", xMax, digits, fwidth, null);
            // gd.setInsets(15, 0, 10);
            gd.addStringField("Y_max:", yMax, fwidth);
            if (stackSize>1)
                gd.addCheckbox("Stack histogram", stackHistogram);
            ArrayList numbers = gd.getNumericFields();
            if (numbers!=null)
            {
                minField = (TextBox)numbers[1];
                minField.TextChanged += textValueChanged;
                maxField = (TextBox)numbers[2];
                maxField.TextChanged += textValueChanged;
                }
            checkbox = (CheckBox)(gd.getCheckboxes()[0]);
            gd.showDialog();
            if (gd.wasCanceled())
                return false;
            nBins = (int)gd.getNextNumber();
            useImageMinAndMax = gd.getNextBoolean();
            xMin = gd.getNextNumber();
            xMax = gd.getNextNumber();
            yMax = gd.getNextString();
            stackHistogram = (stackSize>1) ? gd.getNextBoolean() : false;
            if (!IJ.isMacro())
            {
                if (nBins>=2 && nBins<=1000)
                    HistogramWindow.nBins = nBins;
                staticUseImageMinAndMax = useImageMinAndMax;
                staticXMin=xMin; staticXMax=xMax;
                staticYMax = yMax;
                staticStackHistogram = stackHistogram;
            }
            IJ.register(typeof(Histogram));
		    return true;
	    }

        public void textValueChanged(object? sender, EventArgs e)
        {
            bool rangeChanged = !defaultMin.Equals(minField.Text)
                || !defaultMax.Equals(maxField.Text);
            if (rangeChanged)
                checkbox.Checked = false;
        }

        int setupDialog(ImagePlus imp, int flags)
        {
            int stackSize = imp.getStackSize();
            if (stackSize>1)
            {
                string macroOptions = Macro.GetOptions();
                if (macroOptions!=null)
                {
                    if (macroOptions.IndexOf("stack ")>=0)
                        return flags+BasePlugInFilter.DOES_STACKS;
                    else
                        return flags;
                }

                DialogResult result = MessageBox.Show( "Include all " + stackSize + " images?", "Histogram",
                    MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
                if (result == DialogResult.Yes)
                {
                    // if (Recorder.record)
                    //     Recorder.recordOption("stack");
                    return flags+BasePlugInFilter.DOES_STACKS;
                }
                else
                {
                    return BasePlugInFilter.DONE;
                }
                // YesNoCancelDialog d = new YesNoCancelDialog(IJ.getInstance(),
                //     "Histogram", "Include all "+stackSize+" images?");
                // if (d.cancelPressed())
                //     return PlugInFilter.DONE;
                // else if (d.yesPressed())
                // {
                //     if (Recorder.record)
                //         Recorder.recordOption("stack");
                //     return flags+PlugInFilter.DOES_STACKS;
                // }
                // if (Recorder.record)
                //     Recorder.recordOption("slice");
            }
            return flags;
        }

    }
}
